/*
 *  Copyright 2011 yura.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package org.opu.vcforms.vcapi.dao;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Property;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;

/**
 *
 * @author yura
 */
public abstract class AbstractDao<T extends Serializable, KeyType extends Serializable> {

    /**
     * Method to return the class of the domain object
     */
    protected abstract Class<T> getDomainClass();
    protected HibernateTemplate hibernateTemplate;

    public void setSessionFactory(SessionFactory sf) {
        hibernateTemplate = new HibernateTemplate(sf);
    }

    @SuppressWarnings("unchecked")
    public T load(KeyType id) {
        return (T) hibernateTemplate.load(getDomainClass(), id);
    }

    @SuppressWarnings("unchecked")
    public T get(KeyType id) {
        return (T) hibernateTemplate.get(getDomainClass(), id);
    }

    @SuppressWarnings("unchecked")
    public List<T> getList(final int startIndex, final int maxResult) {
        List<T> results = null;
        if (startIndex > -1 && maxResult > -1) {

            results = (List) hibernateTemplate.execute(new HibernateCallback() {

                @Override
                public Object doInHibernate(Session session)
                        throws HibernateException, SQLException {
                    Criteria crit = session.createCriteria(getDomainClass());
                    crit.setFirstResult(startIndex).setMaxResults(maxResult);
                    return crit.list();
                }
            });
        } else {
            return getList();
        }
        return new ArrayList<T>(new HashSet<T>(results));
    }

    @SuppressWarnings("unchecked")
    public List<T> getList() {
        List<T> results = null;
        results = (List) hibernateTemplate.execute(new HibernateCallback() {

            @Override
            public Object doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Criteria crit = session.createCriteria(getDomainClass());
                return crit.list();
            }
        });

        return new ArrayList<T>(new HashSet<T>(results));
    }

    @SuppressWarnings("unchecked")
    public List<T> search(final DetachedCriteria dCriteria) {
        List<T> results = null;
        results = (List) hibernateTemplate.execute(new HibernateCallback() {

            @Override
            public Object doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Criteria crit = dCriteria.getExecutableCriteria(session);
                return crit.list();
            }
        });

        return new ArrayList<T>(new HashSet<T>(results));
    }

    /**
     * loads Object with defined "equals" attributes and it's values and lazy
     * properties which should be readed eagerly
     */
    public final List<T> searchEagerly(final String[] equalsAttributes,
            final Object[] equalsAttributeValues, final String[] eagerAttributes) {
        DetachedCriteria criteria = DetachedCriteria.forClass(getDomainClass());
        if (equalsAttributes != null) {
            for (int i = 0; i < equalsAttributes.length; i++) {
                criteria.add(Property.forName(equalsAttributes[i]).eq(
                        equalsAttributeValues[i]));
            }
        }
        return findObjectsEagerly(criteria, eagerAttributes);
    }

    /**
     * Vind objecten op basis van de class , criteria en properties die eager
     * geladen moeten worden. Een voorbeeld van samengesteldeEagerProperties is
     * "leveringSpecificatie.bronSysteem.dataLeveringOvereenkomst". Het
     * scheidingsteken is een punt.
     */
    @SuppressWarnings("unchecked")
    public final List<T> findObjectsEagerly(final DetachedCriteria criteria,
            final String[] compositeEagerProperties) {
        List<T> result = (List) hibernateTemplate.execute(new HibernateCallback() {

            @Override
            public Object doInHibernate(Session session)
                    throws SQLException {
                Criteria crit = null;

                if (criteria == null) {
                    crit = session.createCriteria(getDomainClass());
                    // restrict de criteria
                    // crit = insertNullIds(crit);
                } else {
                    // restrict de criteria
                    crit = criteria.getExecutableCriteria(session);
                }

                // Zet de FetchMode op JOIN voor de
                // samengesteldeEagerProperties
                for (int i = 0; (compositeEagerProperties != null)
                        && (i < compositeEagerProperties.length); i++) {
                    // recursief punt-gescheiden-eagerAttributes
                    // toevoegen
                    String[] eagerProperties = compositeEagerProperties[i].split("\\.");
                    String eagerProperty = "";

                    for (int j = 0; j < eagerProperties.length; j++) {
                        eagerProperty += eagerProperties[j];
                        crit = crit.setFetchMode(eagerProperty,
                                FetchMode.JOIN); // zet Fetchmode
                        if (j < eagerProperties.length - 1) {
                            eagerProperty += ".";
                        }
                    }
                }

                List tmpResult = crit.list();

                // make items uniek
                return new ArrayList<T>(new HashSet<T>(tmpResult));
            }
        });

        return result;
    }

    public void save(T object) {
        hibernateTemplate.save(object);
        hibernateTemplate.flush();
        hibernateTemplate.clear();
    }

    /** Save or update the given persistent instance,
     * according to its id.
     * @param entity the persistent instance to save or update
     * @throws DataAccessException in case of Hibernate errors
     */
	public void saveOrUpdate(T object) {
        hibernateTemplate.saveOrUpdate(object);
        hibernateTemplate.flush();
        hibernateTemplate.clear();
    }

    /**
     * Delete the given persistent instance.
     * @param entity the persistent instance to delete
     * @throws org.springframework.dao.DataAccessException in case of Hibernate errors
     */
	public void delete(T object) {
        hibernateTemplate.delete(object);
        hibernateTemplate.flush();
        hibernateTemplate.clear();
    }

    public void deleteAll(Collection<T> objects) {
        hibernateTemplate.deleteAll(objects);
        hibernateTemplate.flush();
        hibernateTemplate.clear();
    }
}
